#include "Renderer.h"
#include "../Globals/Game.h"
#include "../Timer.h"
#include "../ErrorDefinitions.h"
#include "Model.h"
#include "Material.h"
#include "Mesh.h"
#include "ShaderBase.h"
#include "ShaderSkybox.h"
#include "ShaderTexture.h"

#include <fstream>
#include <string>

#include "../tinyxml/tinyxml.h"
#include "../tinyxml/tinyxml.cpp"
#include "../tinyxml/tinystr.h"
#include "../tinyxml/tinystr.cpp"
#include "../tinyxml/tinyxmlerror.cpp"
#include "../tinyxml/tinyxmlparser.cpp"

CRenderer * CRenderer::GetInstance()
{
	static CRenderer instance;
	return &instance;
}

CRenderer::CRenderer()
{
	// Ensuring all pointers start with null.
	// This also ensures the destructor doesn't break when releasing them.
	m_pd3dObject			= nullptr;
	m_pd3dDevice			= nullptr;
	m_pd3dSpriteManager		= nullptr;
	m_pd3dFont				= nullptr;
	m_pd3dPositionBuffer	= nullptr;
	m_pd3dNormalBuffer		= nullptr;
	m_pd3dTexCoordBuffer	= nullptr;
	m_pd3dTangentBuffer		= nullptr;
	m_pd3dLineBuffer		= nullptr;
	m_pd3dLineEffect		= nullptr;
	m_pd3dIndexBuffer		= nullptr;
	m_pd3dVertexDecl		= nullptr;
	m_pTimer				= nullptr;
}

CRenderer::~CRenderer()
{
}

int CRenderer::Initialize(HWND hWnd, unsigned int nWidth, unsigned int nHeight, bool bIsWindowed)
{
	// Create the D3D object
	m_pd3dObject = Direct3DCreate9(D3D_SDK_VERSION);
	if(nullptr == m_pd3dObject) 
	{
		return D3DOBJECT_FAIL_INITIALIZE;
	}

	// Create D3DDevice
	D3DPRESENT_PARAMETERS d3dpp;
	ZeroMemory(&d3dpp, sizeof(D3DPRESENT_PARAMETERS));

	d3dpp.hDeviceWindow				= hWnd;
	d3dpp.Windowed					= bIsWindowed;
	d3dpp.PresentationInterval		= D3DPRESENT_INTERVAL_IMMEDIATE;
	d3dpp.SwapEffect				= D3DSWAPEFFECT_DISCARD;
	d3dpp.AutoDepthStencilFormat	= D3DFMT_D16;
	d3dpp.EnableAutoDepthStencil	= true;

	// Setting our handle
	m_hWindowHandle = hWnd;

	// Create the D3Ddevice
	HRESULT result = m_pd3dObject->CreateDevice(0, D3DDEVTYPE_HAL, hWnd,
		D3DCREATE_HARDWARE_VERTEXPROCESSING, &d3dpp, &m_pd3dDevice);
	if(result != D3D_OK)
	{
		return D3DDEVICE_FAIL_INITIALIZE;
	}

	// Create the SpriteManager
	result = D3DXCreateSprite(m_pd3dDevice, &m_pd3dSpriteManager);
	if(result != D3D_OK)
	{
		return SPRITEMAN_FAIL_INITIALIZE;
	}

	// Create the font
	result = D3DXCreateFont( m_pd3dDevice, 16, 0, FW_NORMAL, NULL, false, 
		DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, 
		ANTIALIASED_QUALITY, DEFAULT_PITCH | FF_DONTCARE, 
		L"Terminal", &m_pd3dFont);

	if(result != D3D_OK)
	{
		return FONT_FAIL_INITIALIZE;
	}

	// Default matrix placement
	D3DXVECTOR3 eye(0 , 3, -5), at(0, 1, 0), up(0, 1, 0);
	D3DXMatrixLookAtLH(&m_d3dCameraMatrix, &eye, &at, &up);
	float fov = (D3DXToRadian(75)), aspect = (nWidth/(float)nHeight), znear = 0.01f, zfar = 100.0f; 
	D3DXMatrixPerspectiveFovLH(&m_d3dProjectionMatrix, fov, aspect, znear, zfar);

	// Setting the world to identity
	D3DXMatrixIdentity(&m_d3dWorldMatrix);


	// Setting the world matrix as our matrix
	m_pd3dDevice->SetTransform(D3DTS_WORLD, &m_d3dWorldMatrix);

	// Getting the game's timer
	m_pTimer = CGame::GetInstance()->GetTimer();


	// Load banks
	// The order banks are loaded in is important, as there are dependencies
	// (Textures, Shaders, Models)

	// Load the texture bank
	result = LoadTextureBank("Resources/Textures/TextureBank.xml");
	if(result != 0)
	{
		return TEXTURE_BANK_FAIL_INITIALIZE;
	}

	// Load the cube map bank
	result = LoadCubeMapBank("Resources/Textures/CubeMapBank.xml");
	if(result != 0)
	{
		return CUBEMAP_BANK_FAIL_INITIALIZE;
	}

	// Load the shader bank
	result = LoadShaderBank("Resources/Shaders/ShaderBank.xml");
	if(result != 0)
	{
		return SHADER_FAIL_INITIALIZE;
	}

	// Load the model bank
	result = LoadModelBank("Resources/Models/ModelBank.xml");
	if(result != 0)
	{
		return MODEL_BANK_FAIL_INITIALIZE;
	}

	// Create the various buffers needed for rendering
	result = CreateBuffers();
	if(result != 0)
	{
		return result;
	}

	// Everything is a success
	return 0;
}

void CRenderer::Shutdown()
{
	// Releasing vertex declaration
	if(m_pd3dVertexDecl)
	{
		m_pd3dVertexDecl->Release();
		m_pd3dVertexDecl = nullptr;
	}

	// Releasing the buffers used for the objects
	if(m_pd3dPositionBuffer)
	{
		m_pd3dPositionBuffer->Release();
		m_pd3dPositionBuffer = nullptr;
	}
	if(m_pd3dIndexBuffer)
	{
		m_pd3dIndexBuffer->Release();
		m_pd3dIndexBuffer = nullptr;
	}
	if(m_pd3dNormalBuffer)
	{
		m_pd3dNormalBuffer->Release();
		m_pd3dNormalBuffer = nullptr;
	}
	if(m_pd3dTexCoordBuffer)
	{
		m_pd3dTexCoordBuffer->Release();
		m_pd3dTexCoordBuffer = nullptr;
	}
	if(m_pd3dLineBuffer)
	{
		m_pd3dLineBuffer->Release();
		m_pd3dLineBuffer = nullptr;
	}
	if(m_pd3dTangentBuffer)
	{
		m_pd3dTangentBuffer->Release();
		m_pd3dTangentBuffer = nullptr;
	}

	// Releasing shaders
	for(unsigned int i = 0; i < m_vd3dShaderBank.size(); ++i)
	{
		if(m_vd3dShaderBank[i] != nullptr)
		{
			m_vd3dShaderBank[i]->Release();
			m_vd3dShaderBank[i] = nullptr;
		}
	}

	// Releasing the textures used by the renderer
	for(unsigned int i = 0; i < m_vd3dTextureBank.size(); ++i)
	{
		if(m_vd3dTextureBank[i].second != nullptr)
		{
			m_vd3dTextureBank[i].second->Release();
			m_vd3dTextureBank[i].second = nullptr;
		}
	}

	// Releasing the cube maps used by the renderer
	for(unsigned int i = 0; i < m_vd3dCubeMapBank.size(); ++i)
	{
		if(m_vd3dCubeMapBank[i].second != nullptr)
		{
			m_vd3dCubeMapBank[i].second->Release();
			m_vd3dCubeMapBank[i].second = nullptr;
		}
	}

	if(m_pd3dLineEffect)
	{
		m_pd3dLineEffect->Release();
		m_pd3dLineEffect = nullptr;
	}

	if(m_pd3dFont)
	{
		m_pd3dFont->Release();
		m_pd3dFont = nullptr;
	}

	// Releasing and shutting down the major systems
	if(m_pd3dSpriteManager)
	{
		m_pd3dSpriteManager->Release();
		m_pd3dSpriteManager = nullptr;
	}
	if(m_pd3dDevice)
	{
		m_pd3dDevice->Release();
		m_pd3dDevice = nullptr;
	}
	if(m_pd3dObject)
	{
		m_pd3dObject->Release();
		m_pd3dObject = nullptr;
	}
}

int CRenderer::CreateBuffers()
{
	// Creating the declarations for each of the buffers
	D3DVERTEXELEMENT9 elements[] =
	{
		// stream, offset, type, method, usage, usageIndex 
		{0, 0, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
		{1, 0, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_NORMAL, 0},
		{2, 0, D3DDECLTYPE_FLOAT2, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_TEXCOORD, 0},
		{3, 0, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_TANGENT, 0},
		D3DDECL_END()
	};
	HRESULT result = m_pd3dDevice->CreateVertexDeclaration(elements, &m_pd3dVertexDecl);

	if(result != D3D_OK)
	{
		return VERTEXDECL_FAIL_INITIALIZE;
	}

	// Create the position buffer with the contents of the Vertices vector
	result = BuildPositionBuffer();
	if(result != 0)
	{
		return result;
	}

	// Create the normal buffer with the contents of the Normals vector
	result = BuildNormalBuffer();
	if(result != 0)
	{
		return result;
	}

	// Create the texcoord buffer with the contents of the TexCoords vector
	result = BuildTexCoordBuffer();
	if(result != 0)
	{
		return result;
	}

	// Create the tangent buffer with the contents of the Tangents vector
	result = BuildTangentBuffer();
	if(result != 0)
	{
		return result;
	}

	// Create the index buffer with the contents of the Indices vector
	result = BuildIndexBuffer();
	if(result != 0)
	{
		return result;
	}


	return 0;
}

int CRenderer::LoadMesh(const char* szFilePath, const char* szMeshName, int nBufferField)
{
	// Load the binary equivalent
	std::fstream in;
	std::string szFileName(szFilePath);

	in.open(szFilePath, std::ios_base::in|std::ios_base::binary);
	if(in.is_open())
	{
		// The model we're loading
		TMesh currMesh;

		// Save name of Mesh (For now, hard coded);
		strcpy_s(currMesh.m_szMeshName, 32, szMeshName);

		// The start indices for each buffer, dependant on how many we have now
		currMesh.m_nVertStartIndex = m_Vertices.size();
		currMesh.m_nIndexStart = m_Indices.size();
		currMesh.m_nNormalStartIndex = m_Normals.size();
		currMesh.m_nTexCoordStartIndex = m_TexCoords.size();
		currMesh.m_nTangentStartIndex = m_Tangents.size();

		// Start loading in the Mesh data
		// Number of verts
		in.read((char*)&currMesh.m_nVertCount, sizeof(currMesh.m_nVertCount));

		// Loop and load each position
		for(unsigned int i = 0; i < currMesh.m_nVertCount; ++i)
		{
			// The new position vector
			D3DXVECTOR3 pos;

			// Read in the position
			in.read((char*)&pos, sizeof(D3DXVECTOR3));

			// Add the position to the array
			m_Vertices.push_back(pos);
		}

		// Loop and load each normal
		for(unsigned int i = 0; i < currMesh.m_nVertCount; ++i)
		{
			// The new position vector
			D3DXVECTOR3 normal;

			// Read in the position
			in.read((char*)&normal, sizeof(D3DXVECTOR3));

			// Add the normal to the array
			m_Normals.push_back(normal);
		}

		// Loop and load each texcoord
		for(unsigned int i = 0; i < currMesh.m_nVertCount; ++i)
		{
			// The new texCoord vector
			D3DXVECTOR2 texCoord;

			// Read in the texCoord
			in.read((char*)&texCoord, sizeof(D3DXVECTOR2));

			// Add the texCoord to the array
			m_TexCoords.push_back(texCoord);
		}

		// Loop and load each tangent
		for(unsigned int i = 0; i < currMesh.m_nVertCount; ++i)
		{
			// The new tangent vector
			D3DXVECTOR3 tangent;

			// Read in the tangent
			in.read((char*)&tangent, sizeof(D3DXVECTOR3));

			// Add the tangent to the array
			m_Tangents.push_back(tangent);
		}

		// Number of primitives and indices
		in.read((char*)&currMesh.m_nPrimCount, sizeof(currMesh.m_nPrimCount));
		currMesh.m_nIndexCount = currMesh.m_nPrimCount * 3;

		// Load in the indices needed
		for(unsigned int i = 0; i < currMesh.m_nIndexCount; ++i)
		{
			// The new index
			unsigned int index;

			// Read in the index
			in.read((char*)&index, sizeof(index));

			// Push the new index into the array
			m_Indices.push_back(index);
		}

		// Closes the Mesh file
		in.close();

		// Push back the new Mesh
		m_MeshBank.push_back(currMesh);

		// Return the index to this mesh
		return m_MeshBank.size() - 1;
	}

	return -1;
}

int CRenderer::LoadModelBank(const char* szFilePath)
{
	// The document
	TiXmlDocument doc;

	// Attempt the load the doc from the filepath
	if(doc.LoadFile(szFilePath) == false)
		return -1;

	// Access the root node in the document
	TiXmlElement* Root = doc.RootElement();
	if(Root == nullptr)
		return -1;

	// Iterate throught the models
	TiXmlElement* modelRoot = Root->FirstChildElement("model");

	while(modelRoot != nullptr)
	{
		// Our model
		CModel model;

		// Get the info of the model
		TiXmlElement* modelInfo = modelRoot->FirstChildElement("info");

		// Load the model name from xml
		const char* text = modelInfo->Attribute("ModelName");
		if(text != nullptr)
		{
			model.SetName(text);
		}

		// Load the shader name from xml
		text = modelInfo->Attribute("ShaderName");
		if(text != nullptr)
		{
			for(unsigned int i = 0; i < m_vRenderSet.size(); ++i)
			{
				if(strcmp(text, m_vRenderSet[i].GetShaderName()) == 0)
				{
					model.SetShaderIndex(i);
				}
			}
		}

		// Check if our mesh is already in the bank
		text = modelInfo->Attribute("MeshName");
		if(text != nullptr)
		{
			for(unsigned int i = 0; i < m_MeshBank.size(); ++i)
			{
				if(strcmp(text, m_MeshBank[i].m_szMeshName) == 0)
				{
					model.SetMeshIndex(i);
					break;
				}
			}
		}

		// If we don't have the mesh loaded, then let's load it
		// This dynamically loads only needed meshes
		if(model.GetMeshIndex() == -1)
		{
			// Get our mesh location from xml
			const char* location = modelInfo->Attribute("MeshLocation");
			if(text != nullptr)
			{
				// Load the needed mesh and catch its index
				int mIndex = LoadMesh(location, text, m_vRenderSet[model.GetShaderIndex()].GetUsageFlags());

				// Update the model's mesh index
				model.SetMeshIndex(mIndex);
			}
		}

		// Now, if the model is textured, load textures
		TiXmlElement* texture = modelRoot->FirstChildElement("texture");
		
		// Confirming we have textures
		if(texture != nullptr)
		{
			// Get the kind of texture we're using so we know which bank to look through
			const char* texDimension = modelInfo->Attribute("TexDimension");

			// Set to first texture for looping
			texture = texture->FirstChildElement("texInfo");

			// While we have textures to load
			while(texture != nullptr)
			{
				// Get the type of texture
				text = texture->Attribute("type");
				if(text != nullptr)
				{
					// Get the name of the texture
					const char* texName = texture->Attribute("name");
					if(texName != nullptr)
					{
						// And look for it in the correct bank
						if(strcmp(texDimension, "2D") == 0)
						{
							for(unsigned int i = 0; i < m_vd3dTextureBank.size(); ++i)
							{
								// Check for the name
								if(strcmp(texName, m_vd3dTextureBank[i].first.c_str()) == 0)
								{
									// Now check what kind of texture we are
									// (diffuse, normal, specular, emissive)
									if(strcmp(text, "diffuse") == 0)
									{
										// We found our material's texture for diffuse
										model.GetMaterial().m_nDiffuseID = i;
									}
									else if(strcmp(text, "emissive") == 0)
									{
										// We found our material's texture for emissive
										model.GetMaterial().m_nEmissiveID = i;
									}
									else if(strcmp(text, "specular") == 0)
									{
										// We found our material's texture for specular
										model.GetMaterial().m_nSpecularID = i;
									}
									else if(strcmp(text, "normal") == 0)
									{
										// We found our material's texture for normal
										model.GetMaterial().m_nNormalID = i;
									}
								}
							}
						}
						else if(strcmp(texDimension, "3D") == 0)
						{
							for(unsigned int i = 0; i < m_vd3dCubeMapBank.size(); ++i)
							{
								// Check for the name
								if(strcmp(texName, m_vd3dCubeMapBank[i].first.c_str()) == 0)
								{
									model.GetMaterial().m_nCubeMapID = i;
								}
							}
						}
					}
				}

				// Move to next texture's info
				texture = texture->NextSiblingElement("texInfo");
			}
		}

		// Determine if specialty shader and make a parameter block
		unsigned int unIndex = model.GetShaderIndex();
		if(strcmp(m_vRenderSet[unIndex].GetShaderName(), "Textured") == 0)
		{
			// Typecast to the shader texture class
			CShaderTexture* tex = (CShaderTexture*)&m_vRenderSet[model.GetShaderIndex()];
			tex->CreateParameterHandle(model.GetMaterial(), unIndex);
		}
		else if(strcmp(m_vRenderSet[unIndex].GetShaderName(), "Skybox") == 0)
		{
			// Typecast to the shader skybox class
			CShaderSkybox* sky = (CShaderSkybox*)&m_vRenderSet[model.GetShaderIndex()];
			sky->CreateParameterHandle(model.GetMaterial(), unIndex);
		}

		// We're done filling out the model. Push it into the bank
		m_vModelBank.push_back(model);

		// Move to the next model (if there is one)
		modelRoot = modelRoot->NextSiblingElement("model");
	}

	// Done loading in all models
	return 0;
}

int CRenderer::LoadShaderBank(const char* szFilePath)
{
	// The pointer
	ID3DXEffect* pd3dShader;
	HRESULT result;

	//// Loading in one standard effect. Simply displays the object.
	//result = D3DXCreateEffectFromFile(m_pd3dDevice, L"Resources/Shaders/Pos.fx", 0, 0, 0, 0, &pd3dShader, 0);
	//if(result != D3D_OK)
	//{
	//	return SHADER_FAIL_INITIALIZE;
	//}

	//// Save that pointer
	//m_vd3dShaderBank.push_back(pd3dShader);
	//pd3dShader = nullptr;

	//// Make a shader object
	//CShaderBaseClass cShader("Pos");
	//m_vRenderSet.push_back(cShader);


	// Read through file, adding each shader and pushing each class into bank.
	// The class will be decided by reading in a name from the file
	// Since we only have one right now, I'll hardcode

	// Displays the object with a directional light.
	result = D3DXCreateEffectFromFile(m_pd3dDevice, L"Resources/Shaders/Skybox.fx", 0, 0, 0, 0, &pd3dShader, 0);
	if(result != D3D_OK)
	{
		return SHADER_FAIL_INITIALIZE;
	}

	// Save that pointer
	m_vd3dShaderBank.push_back(pd3dShader);
	pd3dShader = nullptr;

	// Make a shader object (The Usage will also be read in)
	//if(strcmp("file", "Light") == 0)
	//{
	CShaderSkybox cShader("Skybox", TMesh::MESH_TEXCOORD | TMesh::MESH_POSITION);
	m_vRenderSet.push_back(cShader);
	//}


	// Displays the object with a diffuse texture.
	result = D3DXCreateEffectFromFile(m_pd3dDevice, L"Resources/Shaders/Textured.fx", 0, 0, 0, 0, &pd3dShader, 0);
	if(result != D3D_OK)
	{
		return SHADER_FAIL_INITIALIZE;
	}

	// Save that pointer
	m_vd3dShaderBank.push_back(pd3dShader);
	pd3dShader = nullptr;

	//// Make a shader object (The Usage will also be read in)
	//if(strcmp("file", "Texture") == 0)
	//{
		CShaderTexture cShader1("Textured", TMesh::MESH_TANGENT | TMesh::MESH_TEXCOORD | TMesh::MESH_NORMAL | TMesh::MESH_POSITION);
		m_vRenderSet.push_back(cShader1);
	//}




	// Debug shader. Doesn't go in the bank

	// Loads in one non-standard effect. Displays lines in red (Collision, mostly)
	result = D3DXCreateEffectFromFile(m_pd3dDevice, L"Resources/Shaders/DebugLines.fx", 0, 0, 0, 0, &m_pd3dLineEffect, 0);
	if(result != D3D_OK)
	{
		return SHADER_FAIL_INITIALIZE;
	}

	return 0;
}

int CRenderer::LoadTextureBank(const char* szFilePath)
{
	// The document
	TiXmlDocument doc;

	// Attempt the load the doc from the filepath
	if(doc.LoadFile(szFilePath) == false)
		return -1;

	// Access the root node in the document
	TiXmlElement* Root = doc.RootElement();
	if(Root == nullptr)
		return -1;

	// Iterate throught the models
	TiXmlElement* textureRoot = Root->FirstChildElement("info");

	while(textureRoot != nullptr)
	{
		// The texture pointer
		IDirect3DTexture9* texture;

		// Load the model name from xml
		const char* text = textureRoot->Attribute("TextureName");
		if(text != nullptr)
		{
			// The new texture's info
			std::string str(text);

			// Get the file location of the texture
			text = textureRoot->Attribute("TextureLocation");
			if(text != nullptr)
			{
				// Convert from char to wchar (irritating)
				size_t origsize = strlen(text) + 1;
				const size_t newsize = 100;
				size_t convertedChars = 0;
				wchar_t wcstring[newsize];
				mbstowcs_s(&convertedChars, wcstring, origsize, text, _TRUNCATE);

				// Load the texture, using the texture's location
				HRESULT result = D3DXCreateTextureFromFileW(m_pd3dDevice, wcstring, &texture);
				if(result != S_OK)
				{
					return TEXTURE_LOAD_FAIL;
				}

				// Create a new pair for our bank
				std::pair<std::string, IDirect3DTexture9*> p;

				// The name of the texture
				p.first = str;

				// The texture pointer
				p.second = texture;

				// Insert it into the vector
				m_vd3dTextureBank.push_back(p);
			}
		}

		// Move to next texture
		textureRoot = textureRoot->NextSiblingElement("info");
	}

	// Done loading texture bank
	return 0;
}

int CRenderer::LoadCubeMapBank(const char* szFilePath)
{
	// The document
	TiXmlDocument doc;

	// Attempt the load the doc from the filepath
	if(doc.LoadFile(szFilePath) == false)
		return -1;

	// Access the root node in the document
	TiXmlElement* Root = doc.RootElement();
	if(Root == nullptr)
		return -1;

	// Iterate throught the cube maps
	TiXmlElement* textureRoot = Root->FirstChildElement("info");

	while(textureRoot != nullptr)
	{
		// The cube map pointer
		IDirect3DCubeTexture9* texture;

		// Load the cube map name from xml
		const char* text = textureRoot->Attribute("TextureName");
		if(text != nullptr)
		{
			// The new texture's info
			std::string str(text);

			// Get the file location of the texture
			text = textureRoot->Attribute("TextureLocation");
			if(text != nullptr)
			{
				// Convert from char to wchar (irritating)
				size_t origsize = strlen(text) + 1;
				const size_t newsize = 100;
				size_t convertedChars = 0;
				wchar_t wcstring[newsize];
				mbstowcs_s(&convertedChars, wcstring, origsize, text, _TRUNCATE);

				// Load the texture, using the texture's location
				HRESULT result = D3DXCreateCubeTextureFromFile(m_pd3dDevice, wcstring, &texture);
				if(result != S_OK)
				{
					return TEXTURE_LOAD_FAIL;
				}

				// Create a new pair for our bank
				std::pair<std::string, IDirect3DCubeTexture9*> p;

				// The name of the texture
				p.first = str;

				// The texture pointer
				p.second = texture;

				// Insert it into the vector
				m_vd3dCubeMapBank.push_back(p);
			}
		}

		// Move to next texture
		textureRoot = textureRoot->NextSiblingElement("info");
	}

	// Done loading texture bank
	return 0;
}

int CRenderer::BuildPositionBuffer()
{
	// Ensuring we have Vertices
	if(m_Vertices.size() == 0)
	{
		return 0;
	}

	// Creating a buffer based on the contents of the Vertex vector
	HRESULT result = m_pd3dDevice->CreateVertexBuffer(sizeof(D3DXVECTOR3) * m_Vertices.size(), 0, 0, D3DPOOL_DEFAULT, &m_pd3dPositionBuffer, 0);
	if(result != D3D_OK)
	{
		return CREATE_VERTEX_BUFFER_FAIL;
	}

	// Lock the buffer for use
	void *vram = 0;
	result = m_pd3dPositionBuffer->Lock( 0, 0, &vram, 0);
	if(result != D3D_OK)
	{
		return POSITION_BUFFER_LOCK_FAIL;
	}

	// Move those verts into the vram
	memcpy(vram, &m_Vertices[0], m_Vertices.size() * sizeof(D3DXVECTOR3));

	// Unlock the buffer after using
	result = m_pd3dPositionBuffer->Unlock();
	if(result != D3D_OK)
	{
		return POSITION_BUFFER_UNLOCK_FAIL;
	}

	// Clearing the vector since we no longer need the information
	m_Vertices.clear();

	return 0;
}

int CRenderer::BuildNormalBuffer()
{
	// Ensuring we have Normals
	if(m_Normals.size() == 0)
	{
		return 0;
	}

	// Creating a buffer based on the contents of the Normal vector
	HRESULT result = m_pd3dDevice->CreateVertexBuffer(sizeof(D3DXVECTOR3) * m_Normals.size(), 0, 0, D3DPOOL_DEFAULT, &m_pd3dNormalBuffer, 0);
	if(result != D3D_OK)
	{
		return CREATE_NORMAL_BUFFER_FAIL;
	}

	m_Normals[0] = D3DXVECTOR3(0.0f, 1.0f, 0.0f);
	m_Normals[1] = D3DXVECTOR3(0.0f, 0.0f, 1.0f);
	m_Normals[2] = D3DXVECTOR3(1.0f, 0.0f, 0.0f);

	// Lock the buffer for use
	void *vram = 0;
	result = m_pd3dNormalBuffer->Lock( 0, 0, &vram, 0);
	if(result != D3D_OK)
	{
		return NORMAL_BUFFER_LOCK_FAIL;
	}

	// Move those verts into the vram
	memcpy(vram, &m_Normals[0], m_Normals.size() * sizeof(D3DXVECTOR3));

	// Unlock the buffer after using
	result = m_pd3dNormalBuffer->Unlock();
	if(result != D3D_OK) 
	{
		return NORMAL_BUFFER_UNLOCK_FAIL;
	}

	// Clearing the vector since we no longer need the information
	m_Normals.clear();

	return 0;
}

int CRenderer::BuildTexCoordBuffer()
{
	// Ensuring we have TexCoords
	if(m_TexCoords.size() == 0)
	{
		return 0;
	}

	// Creating a buffer based on the contents of the TexCoords vector
	HRESULT result = m_pd3dDevice->CreateVertexBuffer(sizeof(D3DXVECTOR2) * m_TexCoords.size(), 0, 0, D3DPOOL_DEFAULT, &m_pd3dTexCoordBuffer, 0);
	if(result != D3D_OK)
	{
		return CREATE_TEXCOORD_BUFFER_FAIL;
	}

	// Lock the buffer for use
	void *vram = 0;
	result = m_pd3dTexCoordBuffer->Lock( 0, 0, &vram, 0);
	if(result != D3D_OK)
	{
		return TEXCOORD_BUFFER_LOCK_FAIL;
	}

	// Move those verts into the vram
	int si = m_TexCoords.size() * sizeof(D3DXVECTOR2);
	memcpy(vram, &m_TexCoords[0], si);

	// Unlock the buffer after using
	result = m_pd3dTexCoordBuffer->Unlock();
	if(result != D3D_OK)
	{
		return TEXCOORD_BUFFER_UNLOCK_FAIL;
	}

	// Clearing the vector since we no longer need the information
	m_TexCoords.clear();

	return 0;
}

int CRenderer::BuildTangentBuffer()
{
	// Ensuring we have TexCoords
	if(m_Tangents.size() == 0)
	{
		return 0;
	}

	// Creating a buffer based on the contents of the TexCoords vector
	HRESULT result = m_pd3dDevice->CreateVertexBuffer(sizeof(D3DXVECTOR3) * m_Tangents.size(), 0, 0, D3DPOOL_DEFAULT, &m_pd3dTangentBuffer, 0);
	if(result != D3D_OK)
	{
		return CREATE_TEXCOORD_BUFFER_FAIL;
	}

	// Lock the buffer for use
	void *vram = 0;
	result = m_pd3dTangentBuffer->Lock( 0, 0, &vram, 0);
	if(result != D3D_OK)
	{
		return TEXCOORD_BUFFER_LOCK_FAIL;
	}

	// Move those verts into the vram
	memcpy(vram, &m_Tangents[0], m_Tangents.size() * sizeof(D3DXVECTOR3));

	// Unlock the buffer after using
	result = m_pd3dTangentBuffer->Unlock();
	if(result != D3D_OK)
	{
		return TEXCOORD_BUFFER_UNLOCK_FAIL;
	}

	// Clearing the vector since we no longer need the information
	m_Tangents.clear();

	return 0;
}

int CRenderer::BuildIndexBuffer()
{
	// Ensuring we have Indices
	int size = m_Indices.size();
	if(size == 0)
	{
		return 0;
	}

	// Create an index buffer based off of the contents of Indices
	HRESULT result = m_pd3dDevice->CreateIndexBuffer(sizeof(unsigned int) * size, 0, D3DFMT_INDEX32, D3DPOOL_MANAGED, &m_pd3dIndexBuffer, 0);
	if(result != D3D_OK)
	{
		return CREATE_INDEX_BUFFER_FAIL;
	}

	// Lock the buffer for use
	void *vram = 0;
	result = m_pd3dIndexBuffer->Lock( 0 ,0, &vram, 0);
	if(result != D3D_OK)
	{
		return INDEX_BUFFER_LOCK_FAIL;
	}

	// Move those indices into the vram
	memcpy(vram, &m_Indices[0], size * sizeof(unsigned int));

	// Unlock the buffer after using
	result = m_pd3dIndexBuffer->Unlock();
	if(result != D3D_OK)
	{
		return INDEX_BUFFER_UNLOCK_FAIL;
	}

	// Clearing the vector since we no longer need the information
	m_Indices.clear();

	return 0;
}

int CRenderer::RenderObjects(float fElapsedTime)
{
	// Clearing the backbuffer to black
	HRESULT result = m_pd3dDevice->Clear(0, 0, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, D3DCOLOR_XRGB(50,50,50), 1.0f, 0);
	if(result != D3D_OK)
	{
		return DEVICE_CLEAR_FAIL;
	}

	// Begin drawing the scene
	result = m_pd3dDevice->BeginScene();
	if(result != D3D_OK)
	{
		return BEGIN_SCENE_FAIL;
	}

	// Looping through all shaders in the shader array
	for(unsigned int i = 0; i < m_vRenderSet.size(); ++i)
	{
		// Looping through the meshes in this shader
		for(int j = 0; j < m_vRenderSet[i].GetModelAmount(); ++j)
		{
			// Grab the mesh, model and effect to cut down on access
			CModel* cModel = &m_vRenderSet[i].GetMeshes(j);
			TMesh* tMesh = &m_MeshBank[cModel->GetMeshIndex()];
			ID3DXEffect* pd3dEffect = m_vd3dShaderBank[cModel->GetShaderIndex()];

			// Apply our parameter block
			pd3dEffect->ApplyParameterBlock(cModel->GetMaterial().m_hParameters);

			// Looping through each pass of the current shader
			unsigned passes(0);
			pd3dEffect->Begin(&passes, 0);
			for(unsigned int k = 0; k < passes; ++k)
			{
				// Starting the pass
				pd3dEffect->BeginPass(k);
				{
					// Assigning shader uniforms
					D3DXMATRIX mat = cModel->GetMaterial().m_d3dObjectMatrix;
					pd3dEffect->SetMatrix("gWorld", &(mat));
					pd3dEffect->SetMatrix("gViewProjection", &(m_d3dCameraMatrix * m_d3dProjectionMatrix));
					pd3dEffect->SetFloat("gTime", m_pTimer->GetTime());
					//pd3dEffect->SetTexture("gDiffuseTexture", m_vd3dTextureBank[0].second);
					pd3dEffect->CommitChanges();

					// SetVertexDeclaration to the vertexDecl
					m_pd3dDevice->SetVertexDeclaration(m_pd3dVertexDecl);

					// Set all stream sources
					result = SetStreamSources(tMesh, m_vRenderSet[i].GetUsageFlags());
					if(result != 0)
					{
						return SET_STREAM_SOURCES_FAIL;
					}

					// Set Index buffer
					m_pd3dDevice->SetIndices(m_pd3dIndexBuffer);

					// DrawIndexedPrimitive. The indexed object is a triangle list
					result = m_pd3dDevice->DrawIndexedPrimitive(D3DPT_TRIANGLELIST, 0, 0, tMesh->m_nVertCount, tMesh->m_nIndexStart, tMesh->m_nPrimCount);
					if(result != D3D_OK)
					{
						return DRAW_INDEXED_PRIMITIVE_FAIL;
					}
				}
				// Ending the pass
				pd3dEffect->EndPass();
			}
			// Ending the effect once all passes are complete
			pd3dEffect->End();
		}
	}

	// Render the frame's meta information
	result = RenderFrameInfo();
	if(result != 0)
	{
		return result;
	}

	// Flush the lines in our line buffer
	//FlushLines();

	// End the scene
	result = m_pd3dDevice->EndScene();
	if(result != D3D_OK)
	{
		return END_SCENE_FAIL;
	}

	// Present the backbuffer
	result = m_pd3dDevice->Present(0,0,0,0);
	if(result != D3D_OK)
	{
		return PRESENT_SCENE_FAIL;
	}

	// Empty the shaders' mesh lists after rendering
	for(unsigned int i = 0; i < m_vRenderSet.size(); ++i)
	{
		m_vRenderSet[i].ClearModelList();
	}

	return 0;
}

int CRenderer::SetStreamSources(TMesh* tMesh, int nUsage)
{
	// The results
	HRESULT result;

	// Checking if the mesh uses a position stream
	if(nUsage & TMesh::MESH_POSITION)
	{
		result = m_pd3dDevice->SetStreamSource(0, m_pd3dPositionBuffer, tMesh->m_nVertStartIndex * sizeof(D3DXVECTOR3), sizeof(D3DXVECTOR3));
		if(result != S_OK)
		{
			return -1;
		}
	}

	// Checking if the mesh uses a normal stream
	if(nUsage & TMesh::MESH_NORMAL)
	{
		result = m_pd3dDevice->SetStreamSource(1, m_pd3dNormalBuffer, tMesh->m_nNormalStartIndex * sizeof(D3DXVECTOR3), sizeof(D3DXVECTOR3));
		if(result != S_OK)
		{
			return -1;
		}
	}

	// Checking if the mesh uses a texcoord stream
	if(nUsage & TMesh::MESH_TEXCOORD)
	{
		result = m_pd3dDevice->SetStreamSource(2, m_pd3dTexCoordBuffer, tMesh->m_nTexCoordStartIndex * sizeof(D3DXVECTOR2), sizeof(D3DXVECTOR2));
		if(result != S_OK)
		{
			return -1;
		}
	}

	// Checking if the mesh uses a tangent stream
	if(nUsage & TMesh::MESH_TANGENT)
	{
		result = m_pd3dDevice->SetStreamSource(3, m_pd3dTangentBuffer, tMesh->m_nTangentStartIndex * sizeof(D3DXVECTOR3), sizeof(D3DXVECTOR3));
		if(result != S_OK)
		{
			return -1;
		}
	}


	return 0;
}

int CRenderer::RenderFrameInfo()
{
	// Render text for frame rate, time and frames
	HRESULT result = m_pd3dSpriteManager->Begin(D3DXSPRITE_ALPHABLEND);
	if(result != D3D_OK)
	{
		return SPRITE_BEGIN_FAIL;
	}

	wchar_t buff[128]; static int fr = 0;
	RECT topleft = { 10, 10, 300, 25 };
	wsprintf(buff,L"FRAME:%i", ++fr);
	m_pd3dFont->DrawTextW(m_pd3dSpriteManager,buff,-1,&topleft,DT_LEFT,0xFFFF0000);
	swprintf(buff,128,L"TIME:%f",m_pTimer->GetTime());
	topleft.top += 25; topleft.bottom += 25;
	m_pd3dFont->DrawTextW(m_pd3dSpriteManager,buff,-1,&topleft,DT_LEFT,0xFFFF0000);
	wsprintf(buff,L"FPS:%i",m_pTimer->GetFPS());
	topleft.top += 25; topleft.bottom += 25;
	m_pd3dFont->DrawTextW(m_pd3dSpriteManager,buff,-1,&topleft,DT_LEFT,0xFFFF0000);
	
	// End
	result = m_pd3dSpriteManager->End();
	if(result != D3D_OK)
	{
		return SPRITE_END_FAIL;
	}

	return 0;
}

int CRenderer::GetModelInfo(const char* szModelName, CModel& cModel)
{
	// Loop through the Mesh Bank and find a mesh that matches. Otherwise, return -1
	for(unsigned int i = 0; i < m_vModelBank.size(); ++i)
	{
		// Compare names
		if(strcmp(m_vModelBank[i].GetName(), szModelName) == 0)
		{
			// If you find the model, fill out the Material and Shader info
			CModel* model = &m_vModelBank[i];
			cModel.SetMeshIndex(model->GetMeshIndex());
			cModel.SetName(model->GetName());
			cModel.SetShaderIndex(model->GetShaderIndex());
			cModel.GetMaterial() = model->GetMaterial();
			return 0;
		}
	}

	// Model wasn't found
	return -1;
}

void CRenderer::AddModelToSet(const CModel& cModel)
{
	// This function will be reworked to add this to certain shader's lists
	int nMIndex = cModel.GetMeshIndex();

	if(nMIndex != -1)
	{
		int nSIndex = m_vModelBank[nMIndex].GetShaderIndex();
		m_vRenderSet[nSIndex].AddModelToSet(cModel);
	}
	else
	{
		// Send event stating model cannot be found.
	}
}

void CRenderer::AddLines(D3DXVECTOR3 d3dPointA, D3DXVECTOR3 d3dPointB)
{
	tLineVert vert;
	vert.fX = d3dPointA.x; vert.fY = d3dPointA.y; vert.fZ = d3dPointA.z;
	//m_vLineArray.push_back(vert);

	vert.fX = d3dPointB.x; vert.fY = d3dPointB.y; vert.fZ = d3dPointB.z;
	//m_vLineArray.push_back(vert);
}

void CRenderer::FlushLines()
{
	// Move all lines into our vertex buffer for this frame
	// Ensuring we have Vertices
	if(m_vLineArray.size() == 0)
	{
		return;
	}

	// Creating a buffer based on the contents of the Vertex vector
	m_pd3dDevice->CreateVertexBuffer(sizeof(D3DXVECTOR3) * m_vLineArray.size(), 0, 0, D3DPOOL_DEFAULT, &m_pd3dLineBuffer, 0);

	// Lock the buffer for use
	void *vram = 0;
	m_pd3dLineBuffer->Lock( 0, 0, &vram, 0);

	// Move those verts into the vram
	memcpy(vram, &m_vLineArray[0], m_vLineArray.size() * sizeof(D3DXVECTOR3));

	// Unlock the buffer after using
	m_pd3dLineBuffer->Unlock();


	// Looping through each pass of the current shader
	unsigned passes(0);
	m_pd3dLineEffect->Begin(&passes, 0);
	for(unsigned int k = 0; k < passes; ++k)
	{
		// Starting the pass
		m_pd3dLineEffect->BeginPass(k);
		{
			// Assigning shader uniforms
			m_pd3dLineEffect->SetMatrix("gWorld", &(m_d3dWorldMatrix));
			m_pd3dLineEffect->SetMatrix("gViewProjection", &(m_d3dCameraMatrix * m_d3dProjectionMatrix));
			m_pd3dLineEffect->CommitChanges();

			// SetVertexDeclaration to the vertexDecl
			m_pd3dDevice->SetVertexDeclaration(m_pd3dVertexDecl);

			// Set position stream
			m_pd3dDevice->SetStreamSource(0, m_pd3dLineBuffer, 0, sizeof(D3DXVECTOR3));

			// Draw the lines
			m_pd3dDevice->DrawPrimitive(D3DPT_LINELIST, 0, m_vLineArray.size());
		}
		// Ending the pass
		m_pd3dLineEffect->EndPass();
	}
	// Ending the effect once all passes are complete
	m_pd3dLineEffect->End();

	m_vLineArray.clear();
}